package com.common.interceptor;

import com.common.annotion.QueryTable;
import io.netty.util.internal.ObjectUtil;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.DefaultReflectorFactory;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.ibatis.reflection.factory.ObjectFactory;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.time.LocalDate;
import java.util.zip.DataFormatException;

/**
 * sql拦截器，用于时间类型的分表
 * * * */
@Intercepts({@Signature(
        type = Executor.class,
        method = "query",
        args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}
), @Signature(
        type = Executor.class,
        method = "query",
        args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class})})

//@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
@Component
public class SqlInterceptor implements Interceptor {

    /*这里写被分表的表名*/
    private String tableName="task_data";

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
//        获得拦截到的类
        Executor executor = (Executor)invocation.getTarget();
//        获得拦截到的方法里面的参数
        MappedStatement mappedStatement = (MappedStatement)invocation.getArgs()[0];
        Object parameter = invocation.getArgs()[1];
        RowBounds rowBounds = (RowBounds)invocation.getArgs()[2];
        ResultHandler resultHandler = (ResultHandler)invocation.getArgs()[3];
        CacheKey cacheKey=null;
        BoundSql boundSql=null;
//        判断拦截到的方法的参数个数
        if (invocation.getArgs().length==4){
            boundSql=mappedStatement.getBoundSql(parameter);
        }else {
            boundSql=(BoundSql)invocation.getArgs()[5];
            cacheKey=(CacheKey)invocation.getArgs()[4];
        }
        String id = mappedStatement.getId();
//        获得拦截到的实际的类名和方法名
        String className = id.substring(0, id.lastIndexOf("."));
        String methodName = id.substring(id.lastIndexOf(".") + 1);
        Method[] methods = Class.forName(className).getMethods();
        for (Method method : methods) {
            if (method.getName().equals(methodName)&&method.getAnnotation(QueryTable.class)!=null){
//                通过反射修改boundSql里面的sql的值
                String sql = boundSql.getSql();
                int monthValue = LocalDate.now().getMonthValue();
                String newSql = sql.replace(tableName, tableName + "" + monthValue);
                Field field = boundSql.getClass().getDeclaredField("sql");
                field.setAccessible(true);
                field.set(boundSql,newSql);
            }
        }
        return executor.query(mappedStatement,parameter,rowBounds,resultHandler,cacheKey,boundSql);
    }

/*    @Override
    public Object intercept(Invocation invocation) throws Throwable{
        StatementHandler statementHandler = (StatementHandler)invocation.getTarget();
        BoundSql boundSql = statementHandler.getBoundSql();
        MetaObject metaObject = MetaObject.forObject(statementHandler, SystemMetaObject.DEFAULT_OBJECT_FACTORY
                , SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY, new DefaultReflectorFactory());
        MappedStatement mappedStatement = (MappedStatement)metaObject.getValue("delegate.mappedStatement");
        String id = mappedStatement.getId();
//        跟上述Executor类拦截方法一样操作
        return null;
    }*/
}
